/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

#include "Vector.h"

namespace OptixDotNet {
namespace Math
{
	public value class BoundingBox
	{
	public:
		Vector3 Min;
		Vector3 Max;

		BoundingBox( Vector3 min, Vector3 max )
		{
			Min = min;
			Max = max;
		}

		inline void Invalidate()
		{
			Min = Vector3( FLT_MAX, FLT_MAX, FLT_MAX );
			Max = Vector3( FLT_MIN, FLT_MIN, FLT_MIN );
		}

		inline void AddFloat3( Vector3% p )
		{
			Min = Vector3( fminf(Min.X,p.X), fminf(Min.Y,p.Y), fminf(Min.Z,p.Z) );
			Max = Vector3( fmaxf(Max.X,p.X), fmaxf(Max.Y,p.Y), fmaxf(Max.Z,p.Z) );
		}

		inline void AddBounds( BoundingBox% box )
		{
			for( int i = 0; i < 3; i++ ) 
			{
				Min[i] = ( box.Min[i] < Min[i] ) ? box.Min[i] : Min[i];
				Max[i] = ( box.Max[i] > Max[i] ) ? box.Max[i] : Max[i];
			}
		}

		inline void AddBoundsWithTranslation( BoundingBox box, Vector3% trans )
		{
			box.TranslateSelf( trans );
			for( int i = 0; i < 3; i++ ) 
			{
				Min[i] = ( box.Min[i] < Min[i] ) ? box.Min[i] : Min[i];
				Max[i] = ( box.Max[i] > Max[i] ) ? box.Max[i] : Max[i];
			}
		}

		inline bool Contains( Vector3 p )
		{
			return  p.X >= Min.X && p.X <= Max.X &&
					p.Y >= Min.Y && p.Y <= Max.Y &&
					p.Z >= Min.Z && p.Z <= Max.Z;
		}

		inline void TranslateSelf( Vector3% trans )
		{
			Min += trans;
			Max += trans;
		}

		inline Vector3 Extent()
		{
			return Max - Min;
		}

		inline float Extent( int dim )
		{
			return Max[dim] - Min[dim];
		}

		inline int LongestAxis()
		{
			Vector3 d = Extent();

			if( d.X > d.Y )
				return d.X > d.Z ? 0 : 2;

			return d.Y > d.Z ? 1 : 2;
		}

		inline float MaxExtent()
		{
			return Extent( LongestAxis() );
		}

		property Vector3 Center
		{
			Vector3 get()
			{
				return ( Min + Max ) * 0.5f;
			}
		}

		property bool IsValid
		{
			bool get() 
			{
				return	Min.X <= Max.X && 
						Min.Y <= Max.Y && 
						Min.Z <= Max.Z;
			}
		}

		static property int SizeInBytes { int get() { return System::Runtime::InteropServices::Marshal::SizeOf(BoundingBox::typeid); } }

		static property BoundingBox Invalid
		{
			inline BoundingBox get()
			{
				BoundingBox box;
				box.Invalidate();

				return box;
			}
		}

		/// <summary>
		/// Gets or Sets the Min or Max component, where Min = 0, Max = 1
        /// </summary>
		/// <exception cref="ArgumentOutOfRangeException">Index outside the number of bounding box components.</exception>
		property Vector3 default[int]
		{
			inline Vector3 get ( int index )
			{
				switch( index )
				{
				case 0:
					return Min;
				case 1:
					return Max;
				default:
					throw gcnew System::ArgumentOutOfRangeException( "index", "index outside the number of bounding box components" );
				}
			}
			inline void set( int index, Vector3 x )
			{
				switch( index )
				{
				case 0:
					Min = x;
					break;
				case 1:
					Max = x;
					break;
				default:
					throw gcnew System::ArgumentOutOfRangeException( "index", "Index outside the number of bounding box components" );
				}
			}
		}
	};
}
}